#include <assert.h>
#include <iostream>
#include <sstream>
#include <cstring>
#include "thread_pool.hpp"

namespace sola {

    std::unique_ptr<logger_iface> active_logger = nullptr;

    static const char black[] = {0x1b, '[', '1', ';', '3', '0', 'm', 0};
    static const char red[] = {0x1b, '[', '1', ';', '3', '1', 'm', 0};
    static const char yellow[] = {0x1b, '[', '1', ';', '3', '3', 'm', 0};
    static const char blue[] = {0x1b, '[', '1', ';', '3', '4', 'm', 0};
    static const char normal[] = {0x1b, '[', '0', ';', '3', '9', 'm', 0};

    logger::logger(log_level level)
            : m_level(level) {}

    void
    logger::debug(const std::string &msg, const std::string &file, std::size_t line) {
        if (m_level >= log_level::debug) {
            std::lock_guard<std::mutex> lock(m_mutex);
            std::cout << "[" << black << "DEBUG" << normal << "][sola::logger][" << file << ":" << line << "] " << msg
                      << std::endl;
        }
    }

    void
    logger::info(const std::string &msg, const std::string &file, std::size_t line) {
        if (m_level >= log_level::info) {
            std::lock_guard<std::mutex> lock(m_mutex);
            std::cout << "[" << blue << "INFO " << normal << "][sola::logger][" << file << ":" << line << "] " << msg
                      << std::endl;
        }
    }

    void
    logger::warn(const std::string &msg, const std::string &file, std::size_t line) {
        if (m_level >= log_level::warn) {
            std::lock_guard<std::mutex> lock(m_mutex);
            std::cout << "[" << yellow << "WARN " << normal << "][sola::logger][" << file << ":" << line << "] " << msg
                      << std::endl;
        }
    }

    void
    logger::error(const std::string &msg, const std::string &file, std::size_t line) {
        if (m_level >= log_level::error) {
            std::lock_guard<std::mutex> lock(m_mutex);
            std::cerr << "[" << red << "ERROR" << normal << "][sola::logger][" << file << ":" << line << "] " << msg
                      << std::endl;
        }
    }

    void
    debug(const std::string &msg, const std::string &file, std::size_t line) {
        if (active_logger)
            active_logger->debug(msg, file, line);
    }

    void
    info(const std::string &msg, const std::string &file, std::size_t line) {
        if (active_logger)
            active_logger->info(msg, file, line);
    }

    void
    warn(const std::string &msg, const std::string &file, std::size_t line) {
        if (active_logger)
            active_logger->warn(msg, file, line);
    }

    void
    error(const std::string &msg, const std::string &file, std::size_t line) {
        if (active_logger)
            active_logger->error(msg, file, line);
    }

    static std::string
    get_tid() {
        std::stringstream tmp;
        tmp << std::this_thread::get_id();
        return tmp.str();
    }

    thread_pool::thread_pool(int init_size)
            : m_init_worker_num(init_size),
              m_mutex(),
              m_cond(),
              m_is_started(false) {
        start();
    }

    thread_pool::~thread_pool() {
        if (m_is_started) {
            stop();
        }
    }

    void thread_pool::start() {
        assert(m_workers.empty());
        m_is_started = true;
        m_workers.reserve(m_init_worker_num);
        for (int i = 0; i < m_init_worker_num; ++i) {
            m_workers.push_back(new std::thread(std::bind(&thread_pool::thread_loop, this)));
        }

    }

    void thread_pool::stop() {
        __SOLA_LOG(debug, "thread_pool::stop() stop.");
        {
            std::unique_lock<std::mutex> lock(m_mutex);
            m_is_started = false;
            m_cond.notify_all();
            __SOLA_LOG(debug, "thread_pool::stop() notifyAll().");
        }

        for (tp_worker::iterator it = m_workers.begin(); it != m_workers.end(); ++it) {
            (*it)->join();
            delete *it;
        }
        m_workers.clear();
    }


    void thread_pool::thread_loop() {
        __SOLA_LOG(debug, "thread_pool::threadLoop() tid : " + get_tid() + " start.");
        while (m_is_started) {
            jobStr currentJob = getJob();
            if (currentJob.goToWorkWithPara) {
                currentJob.goToWorkWithPara(currentJob.jobId, currentJob.jobInfo); //if get a job  start to work
            } else if (currentJob.goToWork) {
                currentJob.goToWork();
            }
        }
        __SOLA_LOG(debug, "thread_pool::threadLoop() tid : " + get_tid() + " exit.");
    }
    thread_pool::jobStr thread_pool::getJob() {
        std::unique_lock<std::mutex> lock(m_mutex);
        while (m_jobs.empty() && m_is_started) {
            __SOLA_LOG(debug, "thread_pool::getJob() The jobs is empty!");
            __SOLA_LOG(debug, "thread_pool::getJob() the worker's id is : " + get_tid() + " wait to work.");
            m_cond.wait(lock);
        }

        __SOLA_LOG(debug, "thread_pool::getJob() the worker's id is : " + get_tid() + " is awakened");

        jobStr job;
        tp_worker::size_type job_size = m_jobs.size();
        if (!m_jobs.empty() && m_is_started) {
            job = m_jobs.front();
            m_jobs.pop_front();

            assert(job_size - 1 == m_jobs.size());
        }

        return job;

    }

    void thread_pool::add_job_withPara(const thread_pool::tp_job &job, bool isPriority) {
        jobStr tempJob;
        tempJob.goToWork = job;

        std::unique_lock<std::mutex> lock(m_mutex);

        if (!isPriority) {
            m_jobs.push_back(tempJob);
            std::cout << "added a general job  without parameter!" << "\n";
        } else {
            m_jobs.push_front(tempJob);
            std::cout << "added a priority job without parameter!" << "\n";
        }
        //唤醒某个等待(wait)线程
        m_cond.notify_one();
    }

    void thread_pool::add_job_withPara(const thread_pool::tp_job_withPara &job, const int value,  void* info,
                                       bool isPriority) {
        jobStr tempJob;
        tempJob.goToWorkWithPara = job;
        tempJob.jobId = value;
        if (info != NULL) {
            tempJob.jobInfo = info;
        }

        std::unique_lock<std::mutex> lock(m_mutex);


        if (!isPriority) {
            m_jobs.push_back(tempJob);
            std::cout << "added a general job with parameter jobid:" << value << " info:" << info << "\n";
        } else {
            m_jobs.push_front(tempJob);
            std::cout << "added a priority job with parameter: !" << info  << "\n";
        }
        //唤醒某个等待(wait)线程
        m_cond.notify_one();
    }

}
